import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { OverflowModule } from './overflow.module';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CommonModule } from '@angular/common';
import { OverflowDirective } from './overflow.directive';

@Component({
  template: `
    <div
      [lvTooltipTheme]="lvTooltipTheme"
      [lvTooltipPosition]="lvTooltipPosition"
      [lvTooltipClassName]="lvTooltipClassName"
      [lvContentTpl]="lvContentTpl"
      [lvKeepWhiteSpace]="lvKeepWhiteSpace"
      [lvEllipsisDirection]="lvEllipsisDirection"
      [lvSuffixCount]="lvSuffixCount"
      [lvSuffix]="lvSuffix"
      [lvRows]="lvRows"
      lv-overflow
    ></div>
  `,
})
class TestOverflowComponent {
  lvTooltipTheme = 'light';
  lvTooltipPosition = 'top';
  lvTooltipClassName = '';
  lvContentTpl = 'overflow';
  lvKeepWhiteSpace = true;
  lvEllipsisDirection = 'end';
  lvSuffixCount: number;
  lvSuffix: string;
  lvRows = 0;
}

describe('OverflowDirective', () => {
  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        imports: [OverflowModule],
        declarations: [TestOverflowComponent],
        teardown: { destroyAfterEach: false },
      }).compileComponents;
    })
  );
  describe('render', () => {
    let component: TestOverflowComponent;
    let fixture: ComponentFixture<TestOverflowComponent>;

    beforeEach(() => {
      fixture = TestBed.createComponent(TestOverflowComponent);
      component = fixture.componentInstance;
      component.lvTooltipTheme = 'light';
      fixture.detectChanges();
    });

    // 按主题渲染
    it('theme should light render correctly', () => {
      component.lvTooltipTheme = 'light';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
    });
    it('theme should dark render correctly', () => {
      component.lvTooltipTheme = 'dark';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
    });

    // position位置渲染
    it('position should render correctly', () => {
      component.lvTooltipPosition = 'top';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();

      component.lvTooltipPosition = 'bottom';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();

      component.lvTooltipPosition = 'left';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();

      component.lvTooltipPosition = 'right';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
    });

    //  ellipsis省略号位置渲染
    it('ellipsis should render correctly', () => {
      component.lvEllipsisDirection = 'end';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();

      component.lvEllipsisDirection = 'start';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();

      component.lvEllipsisDirection = 'mid';
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
    });
  });
  describe('basic', () => {
    let component: TestOverflowComponent;
    let fixture: ComponentFixture<TestOverflowComponent>;
    const tooltipClass = '.lv-tooltip-inner';

    beforeEach(() => {
      fixture = TestBed.createComponent(TestOverflowComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });
    it('with lvContentTpl', () => {
      const tooltip = document.querySelectorAll(tooltipClass);
      expect(tooltip).toMatchSnapshot();
    });
  });
});

@Component({
  template: `
    <div style="width: 200px;" lv-overflow lvRows="3" class="lv-overflow-case">The story begins as Don Vito Corleone, the head of a New York Mafia family, overseeshis daughter's wedding.</div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestOverflowFuncComponent {
  @ViewChild(OverflowDirective) overflowDirective: OverflowDirective;
}

let fixture: ComponentFixture<TestOverflowFuncComponent>;
const detectChange = () => {
  fixture.detectChanges();
  tick(500);
  fixture.detectChanges();
  tick(500);
  fixture.detectChanges();
  tick(500);
};

describe('OverflowFuncComponent', () => {
  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [TestOverflowFuncComponent],
        imports: [CommonModule, OverflowModule, NoopAnimationsModule],
        teardown: { destroyAfterEach: true },
      }).compileComponents();
    })
  );

  describe('function', () => {

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestOverflowFuncComponent);
      detectChange();
    }));

    it('open', fakeAsync(() => {
      const dom = document.querySelector('.lv-overflow-case');
      jest.spyOn(dom, 'getBoundingClientRect').mockImplementation(() => (dom.classList.contains('lv-text-overflow') ? {
        'x': 387,
        'y': 358,
        'width': 200,
        'height': 66,
        'top': 358,
        'right': 587,
        'bottom': 424,
        'left': 387
      } : {
        'x': 387,
        'y': 556,
        'width': 200,
        'height': 88,
        'top': 556,
        'right': 587,
        'bottom': 644,
        'left': 387
      }) as any);
      dom.dispatchEvent(new Event('mouseenter'));
      detectChange();
      expect(document.querySelector('.lv-tooltip-container')).toBeTruthy();
      document.dispatchEvent(new Event('mouseover'));
      detectChange();
      dom.dispatchEvent(new Event('mouseleave'));
      detectChange();
      expect(document.querySelector('.lv-tooltip-container')).toBeFalsy();
    }));
  });
});

@Component({
  template: `
    <div style="width:200px">
      <span lv-overflow lvTooltipPosition="right" lvEllipsisDirection="mid" lvRows="3" [lvSuffix]="lvSuffix" >
      The story begins as Don Vito Corleone, the head of a New York Mafia family, overseeshis daughter's wedding.
      </span>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestOverflowMidFuncComponent {
  @ViewChild(OverflowDirective) overflowDirective: OverflowDirective;
  lvSuffix = '--xxx';
}

describe('OverflowMidFuncComponent', () => {
  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [TestOverflowMidFuncComponent],
        imports: [CommonModule, OverflowModule, NoopAnimationsModule],
        teardown: { destroyAfterEach: true },
      }).compileComponents();
    })
  );

  describe('function', () => {

    let instance: OverflowDirective;
    let component: TestOverflowMidFuncComponent;
    let fixture: ComponentFixture<TestOverflowMidFuncComponent>;
    const detectChange = () => {
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      tick(500);
    };
    beforeEach(fakeAsync(() => {
      Object.defineProperty(window, 'getComputedStyle', {
        value: () => ({
          getPropertyValue: () => '22px'
        })
      });
      fixture = TestBed.createComponent(TestOverflowMidFuncComponent);
      detectChange();
      component = fixture.componentInstance;
      instance = fixture.componentInstance.overflowDirective;
    }));

    it('open', fakeAsync(() => {
      expect(fixture).toMatchSnapshot();
      instance.lvSuffix = undefined;
      const getDom = () => document.querySelector('[lv-overflow]') as HTMLElement;
      jest.spyOn(getDom(), 'offsetHeight', 'get').mockImplementation(() => {
        const rowHeight = 22;
        const dom = (getDom().querySelector('span') || getDom()) as HTMLElement;
        if (getDom().querySelector('span')) {
          Object.defineProperties(dom, {
            innerText: {
              set: (value: string) => {
                dom.textContent = value;
              }
            }
          });
        }
        return rowHeight * Math.ceil(dom.textContent.length / 29);
      });
      instance.ngAfterViewInit();
      detectChange();
      expect(fixture).toMatchSnapshot();
    }));
  });
});
